<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>数据映射工具 - 源表到目标表字段映射</title>
    <link href="./bootstrap.min.css" rel="stylesheet">
    <link href="./bootstrap-icons.css" rel="stylesheet">
    <style>
        /* 全局样式优化 */
        html, body {
            height: 100%;
            margin: 0;
            padding: 0;
        }

        body {
            padding-bottom: 20px;
            min-height: 100vh;
            box-sizing: border-box;
        }

        .container-fluid {
            min-height: calc(100vh - 40px);
        }

        .field-item {
            margin: 2px 0;
            padding: 6px 10px;
            border: 1px solid #e0e0e0;
            border-radius: 3px;
            cursor: pointer;
            background: white;
            transition: all 0.2s ease;
            font-size: 12px;
            line-height: 1.2;
            display: flex;
            align-items: center;
            min-height: 32px;
        }

        .field-item:hover {
            background: #f8f9fa;
            border-color: #007bff;
            transform: translateX(2px);
        }

        .field-item.dragging {
            opacity: 0.6;
            transform: translateX(-5px);
        }

        .field-item.required {
            border-left: 3px solid #dc3545;
            background: #fff5f5;
        }

        .field-item.optional {
            border-left: 3px solid #28a745;
            background: #f8fff8;
        }

        .field-item.mapped {
            background: #e8f5e8;
            border-color: #4caf50;
            font-weight: 500;
        }

        .field-item.selected {
            background: #fff3cd;
            border-color: #ffc107;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }

      .field-item.selected-b-field {
            background: #e3f2fd !important;
            border: 2px solid #2196f3 !important;
            transform: translateY(-1px);
            box-shadow: 0 4px 12px rgba(33,150,243,0.3);
        }

        .field-name {
            flex: 0 0 120px;
            font-weight: 500;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        .field-type {
            flex: 0 0 80px;
            color: #666;
            font-family: monospace;
            font-size: 11px;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        .field-comment {
            flex: 1;
            color: #666;
            font-size: 11px;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        .field-required {
            color: #dc3545;
            font-size: 10px;
            margin-right: 4px;
        }

        .drop-zone {
            min-height: 400px;
            border: 1px solid #e0e0e0;
            border-radius: 4px;
            padding: 8px;
            background: #fafafa;
            position: relative;
        }

        .drop-zone.drag-over {
            background: #e3f2fd;
            border-color: #2196f3;
        }

        .table-info {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 8px 12px;
            border-radius: 4px;
            margin-bottom: 8px;
        }

        .table-info h5 {
            margin: 0;
            font-size: 14px;
        }

        .table-info p {
            margin: 2px 0 0 0;
            font-size: 12px;
            opacity: 0.9;
        }

        .sql-output {
            background: #f8f9fa;
            border: 1px solid #dee2e6;
            border-radius: 4px;
            padding: 10px;
            font-family: 'Courier New', monospace;
            white-space: pre-wrap;
            max-height: 300px;
            overflow-y: auto;
            font-size: 12px;
        }

        .nav-tabs .nav-link {
            padding: 0.5rem 1rem;
            font-size: 13px;
        }

        .nav-tabs .nav-link.active {
            background-color: #667eea;
            border-color: #667eea;
            color: white;
        }

        .excel-import-area {
            border: 2px dashed #007bff;
            border-radius: 8px;
            padding: 30px;
            text-align: center;
            background: #f8f9ff;
            transition: all 0.3s;
        }

        .excel-import-area:hover {
            background: #e6f3ff;
            border-color: #0056b3;
        }

        .batch-comment-input {
            min-height: 100px;
            font-family: monospace;
            font-size: 12px;
        }

        /* 连线相关样式 */
        #connectionSvg {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            pointer-events: none;
            z-index: 10;
        }

        .mapping-connection {
            stroke: #667eea;
            stroke-width: 2;
            fill: none;
            opacity: 0.7;
            cursor: pointer;
            pointer-events: stroke;
            transition: opacity 0.2s;
        }

        .mapping-connection:hover {
            opacity: 1;
            stroke-width: 3;
        }

        .mapping-connection.highlighted {
            stroke: #ff6b6b;
            stroke-width: 3;
            opacity: 1;
        }

        /* 映射界面布局优化 */
        .mapping-workspace {
            display: flex;
            gap: 20px;
            height: calc(100vh - 200px);
            min-height: 500px;
            position: relative;
        }

        .mapping-column {
            flex: 1;
            display: flex;
            flex-direction: column;
        }

        .connection-area {
            width: 200px;
            position: relative;
            flex-shrink: 0;
        }

        /* 连线相关样式 */
        #connectionSvg {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            z-index: 10;
        }

        .mapping-connection {
            stroke: #667eea;
            stroke-width: 2;
            fill: none;
            opacity: 0.7;
            cursor: pointer;
            transition: all 0.2s ease;
            marker-end: url(#arrowhead);
        }

        .mapping-connection:hover {
            opacity: 1;
            stroke-width: 3;
        }

        .mapping-connection.selected {
            stroke: #ff6b6b;
            stroke-width: 3;
            opacity: 1;
        }

        .connection-line {
            stroke: #667eea;
            stroke-width: 2;
            fill: none;
            opacity: 0.7;
            cursor: pointer;
            transition: all 0.2s ease;
        }

        .connection-point {
            width: 12px;
            height: 12px;
            background: #667eea;
            border: 2px solid white;
            border-radius: 50%;
            cursor: pointer;
            transition: all 0.2s ease;
            position: absolute;
            top: 50%;
            transform: translateY(-50%);
            z-index: 10;
        }

        .connection-point:hover {
            background: #5a67d8;
            transform: translateY(-50%) scale(1.2);
        }

        .connection-point.source {
            background: #28a745;
        }

        .connection-point.target {
            background: #007bff;
        }

      .left-connection {
            left: -6px;
        }

      .right-connection {
            right: -6px;
        }

      /* 表格映射容器样式 */
      .table-mapping-container {
            height: calc(100vh - 250px);
            min-height: 400px;
            max-height: 800px;
            border: 1px solid #dee2e6;
            border-radius: 8px;
            overflow: hidden;
            background: white;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
        }

      .mapping-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 15px 20px;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border-bottom: 2px solid #5a67d8;
        }

      .mapping-header h5 {
            margin: 0;
            font-weight: 600;
            display: flex;
            align-items: center;
            gap: 8px;
        }

      .header-controls {
            display: flex;
            gap: 8px;
        }

      .mapping-content {
            display: flex;
            height: calc(100% - 70px);
        }

      /* 左侧目标表区域 (70%) */
      .target-fields-area {
            width: 70%;
            border-right: 1px solid #dee2e6;
            display: flex;
            flex-direction: column;
        }

      .area-header {
            padding: 12px 20px;
            background: #f8f9fa;
            border-bottom: 1px solid #e9ecef;
        }

      .area-header h6 {
            margin: 0 0 10px 0;
            font-weight: 600;
            color: #495057;
            display: flex;
            align-items: center;
            gap: 6px;
        }

      .area-header .filter-controls {
            display: flex;
            gap: 10px;
            align-items: center;
        }

      .search-input {
            width: 200px;
        }

        /* 必填字段标记样式 */
        .badge.bg-danger {
            font-size: 9px;
            padding: 2px 4px;
            border-radius: 3px;
        }

        .field-name .badge {
            margin-left: 4px;
        }

        .source-field-required {
            display: inline-block;
            margin-left: 4px;
        }

        /* 拖拽高亮样式 */
        .drag-target-highlight {
            background-color: #e7f3ff !important;
            border: 2px solid #0066cc !important;
            box-shadow: 0 0 8px rgba(0, 102, 204, 0.4) !important;
        }

        .target-field-highlight {
            background-color: #fff3cd !important;
            border: 2px solid #ffc107 !important;
            box-shadow: 0 0 12px rgba(255, 193, 7, 0.6) !important;
            animation: pulse-highlight 1.5s infinite;
        }

        @keyframes pulse-highlight {
            0% {
                box-shadow: 0 0 12px rgba(255, 193, 7, 0.6);
            }
            50% {
                box-shadow: 0 0 20px rgba(255, 193, 7, 0.9);
            }
            100% {
                box-shadow: 0 0 12px rgba(255, 193, 7, 0.6);
            }
        }

        .drag-tooltip {
            position: absolute;
            top: 100%;
            left: 0;
            z-index: 1000;
            background: #fff;
            border: 1px solid #ccc;
            border-radius: 4px;
            padding: 8px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.2);
            font-size: 12px;
            min-width: 200px;
        }

        .drag-tooltip-content {
            line-height: 1.4;
        }

        .fields-table tr {
            position: relative;
        }

      .fields-table-container {
            flex: 1;
            overflow-y: auto;
        }

      .fields-table {
            margin: 0;
            font-size: 13px;
        }

      .fields-table th {
            position: sticky;
            top: 0;
            background: #f8f9fa;
            border-bottom: 2px solid #dee2e6;
            font-weight: 600;
            font-size: 12px;
            text-transform: uppercase;
            letter-spacing: 0.5px;
            z-index: 10;
        }

      .fields-table td {
            vertical-align: middle;
            padding: 8px 12px;
            border-bottom: 1px solid #e9ecef;
        }

      .fields-table tbody tr {
            transition: all 0.2s ease;
            cursor: pointer;
        }

      .fields-table tbody tr:hover {
            background-color: #f8f9fa;
            transform: translateX(2px);
        }

      .fields-table tbody tr.matched {
            background-color: #d4edda;
            border-left: 3px solid #28a745;
        }

      .fields-table tbody tr.matched:hover {
            background-color: #c3e6cb;
        }

      .field-name {
            font-weight: 600;
            color: #2c3e50;
            font-family: 'Monaco', 'Menlo', monospace;
            font-size: 12px;
        }

      .field-type {
            font-size: 11px;
            color: #6c757d;
            background: #f8f9fa;
            padding: 2px 6px;
            border-radius: 3px;
            font-family: 'Monaco', 'Menlo', monospace;
        }

      .field-comment {
            color: #6c757d;
            font-style: italic;
            font-size: 12px;
        }

      .mapped-field {
            background: #e8f5e8;
            border: 1px solid #28a745;
            border-radius: 4px;
            padding: 4px 8px;
            font-size: 12px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

      .mapped-field-name {
            font-weight: 600;
            color: #155724;
            font-family: 'Monaco', 'Menlo', monospace;
        }

      .mapped-field-type {
            font-size: 10px;
            color: #6c757d;
            background: white;
            padding: 1px 4px;
            border-radius: 2px;
        }

      /* 右侧源表区域 (30%) */
      .source-fields-area {
            width: 30%;
            display: flex;
            flex-direction: column;
            background: #f8f9fa;
        }

      .source-fields-list {
            flex: 1;
            overflow-y: auto;
            padding: 10px;
        }

      .source-field-item {
            background: white;
            border: 1px solid #dee2e6;
            border-radius: 6px;
            padding: 12px;
            margin-bottom: 8px;
            cursor: grab;
            transition: all 0.2s ease;
            box-shadow: 0 1px 3px rgba(0,0,0,0.1);
      }

      .source-field-item:hover {
            border-color: #007bff;
            box-shadow: 0 2px 8px rgba(0,123,255,0.2);
            transform: translateY(-2px);
        }

      .source-field-item.dragging {
            opacity: 0.5;
            cursor: grabbing;
            transform: rotate(2deg);
            box-shadow: 0 4px 12px rgba(0,0,0,0.3);
        }

      .source-field-item.mapped {
            background: #e8f5e8;
            border-color: #28a745;
            opacity: 0.7;
        }

      .source-field-item {
            background: white;
            border: 1px solid #dee2e6;
            border-radius: 6px;
            padding: 10px;
            margin-bottom: 6px;
            cursor: grab;
            transition: all 0.2s ease;
            box-shadow: 0 1px 3px rgba(0,0,0,0.1);
        }

        .source-field-first-row {
            display: flex;
            align-items: center;
            gap: 8px;
            margin-bottom: 4px;
        }

      .source-field-name {
            font-weight: 600;
            color: #2c3e50;
            font-family: 'Monaco', 'Menlo', monospace;
            font-size: 12px;
            flex: 1;
        }

      .source-field-type {
            font-size: 10px;
            color: #6c757d;
            background: #f8f9fa;
            padding: 1px 4px;
            border-radius: 2px;
            font-family: 'Monaco', 'Menlo', monospace;
        }

      .source-field-required {
            color: #dc3545;
            font-size: 12px;
            font-weight: bold;
        }

      .source-field-comment {
            font-size: 11px;
            color: #868e96;
            font-style: italic;
            line-height: 1.3;
        }

        .source-field-reference-count {
            position: absolute;
            top: 5px;
            right: 5px;
            background: #ffc107;
            color: #000;
            font-size: 10px;
            font-weight: bold;
            padding: 2px 6px;
            border-radius: 10px;
            z-index: 10;
        }

      .drop-target {
            border: 2px dashed #007bff !important;
            background: #e3f2fd !important;
        }

      .drop-target.drag-over {
            border-color: #28a745 !important;
            background: #d4edda !important;
        }

      .terminal-point {
            position: absolute;
            top: 50%;
            transform: translateY(-50%);
            display: flex;
            flex-direction: column;
            align-items: center;
            z-index: 10;
        }

      .left-terminal {
            left: 10px;
        }

      .right-terminal {
            right: 10px;
        }

      .terminal-label {
            width: 40px;
            height: 40px;
            background: #007bff;
            color: white;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            font-weight: bold;
            font-size: 18px;
            box-shadow: 0 4px 8px rgba(0,123,255,0.3);
        }

      .terminal-dot {
            width: 16px;
            height: 16px;
            background: #28a745;
            border: 3px solid white;
            border-radius: 50%;
            margin-top: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.2);
        }

      /* D3字段样式 */
      .d3-field {
            margin: 3px 0;
            padding: 8px 30px 8px 12px;
            background: white;
            border: 1px solid #dee2e6;
            border-radius: 4px;
            cursor: grab;
            transition: all 0.2s ease;
            font-size: 12px;
            line-height: 1.3;
            position: relative;
        }

      /* 右表字段（源表）需要左侧空间给端点 */
      .right-panel .d3-field {
            padding-left: 30px;
            padding-right: 12px;
        }

      .d3-field:hover {
            border-color: #007bff;
            box-shadow: 0 2px 4px rgba(0,123,255,0.2);
            transform: translateX(2px);
        }

      .d3-field.dragging {
            opacity: 0.5;
            cursor: grabbing;
            transform: scale(1.05);
            box-shadow: 0 8px 16px rgba(0,0,0,0.2);
            z-index: 1000;
        }

      .d3-field.mapped {
            background: linear-gradient(135deg, #e8f5e8 0%, #f0f8f0 100%);
            border: 2px solid #28a745;
            border-left: 4px solid #28a745;
            font-weight: 600;
            box-shadow: 0 2px 8px rgba(40,167,69,0.3);
        }

      .d3-field.mapped::before {
            content: '✓';
            position: absolute;
            top: 50%;
            left: 8px;
            transform: translateY(-50%);
            color: #28a745;
            font-weight: bold;
            font-size: 14px;
        }

      .d3-field.selected {
            background: linear-gradient(135deg, #e3f2fd 0%, #f0f8ff 100%);
            border: 2px solid #2196f3;
            box-shadow: 0 4px 12px rgba(33,150,243,0.3);
            transform: translateY(-1px);
        }

      /* 左表字段（目标表）的右侧端点 */
      .left-panel .d3-field::after {
            content: '';
            position: absolute;
            top: 50%;
            right: -8px;
            transform: translateY(-50%);
            width: 12px;
            height: 12px;
            background: #6c757d;
            border: 3px solid white;
            border-radius: 50%;
            box-shadow: 0 3px 6px rgba(0,0,0,0.3);
            z-index: 15;
            transition: all 0.3s ease;
            cursor: crosshair;
        }

      .left-panel .d3-field:hover::after {
            background: #007bff;
            transform: translateY(-50%) scale(1.3);
            box-shadow: 0 4px 8px rgba(0,123,255,0.5);
        }

      .left-panel .d3-field.mapped::after {
            background: #28a745;
            box-shadow: 0 3px 6px rgba(40,167,69,0.4);
        }

      .left-panel .d3-field.mapped:hover::after {
            background: #218838;
            transform: translateY(-50%) scale(1.3);
            box-shadow: 0 4px 8px rgba(40,167,69,0.6);
        }

      /* 右表字段（源表）的左侧端点 */
      .right-panel .d3-field::before {
            content: '';
            position: absolute;
            top: 50%;
            left: -8px;
            transform: translateY(-50%);
            width: 12px;
            height: 12px;
            background: #6c757d;
            border: 3px solid white;
            border-radius: 50%;
            box-shadow: 0 3px 6px rgba(0,0,0,0.3);
            z-index: 15;
            transition: all 0.3s ease;
            cursor: crosshair;
        }

      .right-panel .d3-field:hover::before {
            background: #ff6b35;
            transform: translateY(-50%) scale(1.3);
            box-shadow: 0 4px 8px rgba(255,107,53,0.5);
        }

      .right-panel .d3-field.mapped::before {
            background: #28a745;
            box-shadow: 0 3px 6px rgba(40,167,69,0.4);
        }

      .right-panel .d3-field.mapped:hover::before {
            background: #218838;
            transform: translateY(-50%) scale(1.3);
            box-shadow: 0 4px 8px rgba(40,167,69,0.6);
        }

      .d3-field-content {
            display: flex;
            align-items: center;
            gap: 8px;
        }

      .d3-field-name {
            font-weight: 500;
            color: #495057;
            flex: 0 0 120px;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }

      .d3-field-type {
            font-family: monospace;
            color: #6c757d;
            font-size: 11px;
            flex: 0 0 80px;
        }

      .d3-field-comment {
            color: #6c757d;
            font-size: 11px;
            flex: 1;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }

      .d3-field-required {
            color: #dc3545;
            margin-right: 4px;
        }

      /* D3连接线样式 */
      .d3-connection {
            stroke: #666;
            stroke-width: 1;
            fill: none;
            pointer-events: stroke;
            cursor: pointer;
            transition: all 0.2s ease;
            stroke-dasharray: 4,2;
            opacity: 0.8;
        }

      .d3-connection:hover {
            stroke: #333;
            stroke-width: 2;
            opacity: 1;
        }

      .d3-connection.highlighted {
            stroke: #dc3545 !important;
            stroke-width: 2 !important;
            opacity: 1 !important;
            stroke-dasharray: 4,2 !important;
            filter: drop-shadow(0 0 4px rgba(220, 53, 69, 0.6));
        }

      .d3-connection.selected-field {
            stroke: #dc3545 !important;
            stroke-width: 2 !important;
            opacity: 1 !important;
            stroke-dasharray: 4,2 !important;
        }

        /* 选中表时的连线高亮样式 */
        .d3-connection.table-a-selected,
        .d3-connection.table-b-selected {
            stroke: #dc3545 !important;
            stroke-width: 1.5 !important;
            opacity: 1 !important;
            stroke-dasharray: 3,2 !important;
            animation: pulse-connection 2s infinite;
        }

        @keyframes pulse-connection {
            0% { opacity: 0.7; }
            50% { opacity: 1; }
            100% { opacity: 0.7; }
        }

      .d3-connection-line {
            stroke: #666;
            stroke-width: 1;
            fill: none;
            pointer-events: none;
            stroke-dasharray: 5,3;
            opacity: 0.8;
        }

      .d3-temp-connection {
            stroke: #ff6b35;
            stroke-width: 1;
            fill: none;
            stroke-dasharray: 4,2;
            pointer-events: none;
            opacity: 0.9;
        }

        .mapping-controls {
            background: #f8f9fa;
            padding: 8px;
            border-radius: 8px;
            margin-bottom: 10px;
            border: 1px solid #dee2e6;
        }

        .batch-comment-mapping {
            min-height: 100px;
            font-family: monospace;
            font-size: 13px;
        }

        /* 数据库路径显示 */
        .db-info {
            position: fixed;
            bottom: 10px;
            right: 10px;
            background: rgba(0,0,0,0.7);
            color: white;
            padding: 5px 10px;
            border-radius: 4px;
            font-size: 12px;
            z-index: 1000;
        }

        /* 字段卡片紧凑化 */
        .field-card small {
            font-size: 11px;
        }

        .field-card .bi-asterisk {
            font-size: 10px;
        }

        /* 搜索和筛选样式 */
        .table-info .form-control,
        .table-info .form-select {
            font-size: 13px;
        }

        .table-info .input-group-text {
            font-size: 13px;
        }

        /* 右键菜单样式 */
        #connectionContextMenu {
            min-width: 150px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.15);
        }

        /* 字段过滤动画 */
        .field-card {
            transition: all 0.2s ease-in-out;
        }

        .field-card.filtered-out {
            display: none;
        }

        .field-card.search-highlight {
            background: #fff3cd;
            border-color: #ffc107;
        }

        /* 统一图表容器样式 */
        .unified-mapping-container {
            display: flex;
            flex-direction: column;
            gap: 15px;
            height: calc(100vh - 200px);
            min-height: 500px;
        }

        .unified-sankey-container {
            flex: 1;
            position: relative;
            background: #fafafa;
            border: 1px solid #e0e0e0;
            border-radius: 0 0 8px 8px;
            overflow: hidden;
            min-height: 500px;
        }

        #g6SankeyContainer {
            width: 100%;
            height: 100%;
        }

        .chart-controls-panel {
            background: white;
            border: 1px solid #e0e0e0;
            border-radius: 8px;
            padding: 15px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.05);
        }

        .control-section h6 {
            margin: 0 0 10px 0;
            font-size: 14px;
            font-weight: 600;
            display: flex;
            align-items: center;
            gap: 5px;
        }

        .control-section .btn-group {
            margin-right: 10px;
        }

        .control-section .input-group {
            display: inline-flex;
            width: auto;
            margin-right: 10px;
        }

        .control-section .input-group .form-control {
            width: 200px;
        }

        /* G6增强版节点样式 */
        .g6-field-node {
            cursor: pointer;
        }

        .g6-field-node:hover .field-node-rect {
            filter: brightness(0.95) drop-shadow(0 4px 8px rgba(0,0,0,0.15));
            transform: translateY(-1px);
        }

        .g6-field-node.selected .field-node-rect {
            stroke: #2196f3 !important;
            stroke-width: 3 !important;
            filter: drop-shadow(0 0 12px rgba(33,150,243,0.5));
            transform: translateY(-2px);
        }

        .g6-field-node.selected .table-label-bg {
            filter: brightness(1.2);
        }

        .g6-field-node.can-drop .field-node-rect {
            stroke: #28a745 !important;
            stroke-width: 3 !important;
            filter: drop-shadow(0 0 15px rgba(40,167,69,0.7));
            animation: pulse-drop 1s infinite;
        }

        @keyframes pulse-drop {
            0% {
                transform: scale(1);
                filter: drop-shadow(0 0 8px rgba(40,167,69,0.4));
            }
            50% {
                transform: scale(1.02);
                filter: drop-shadow(0 0 15px rgba(40,167,69,0.7));
            }
            100% {
                transform: scale(1);
                filter: drop-shadow(0 0 8px rgba(40,167,69,0.4));
            }
        }

        .g6-field-node.dragging .field-node-rect {
            stroke: #ff6b35 !important;
            stroke-width: 3 !important;
            filter: drop-shadow(0 0 15px rgba(255,107,53,0.7));
            opacity: 0.8;
            transform: scale(1.05);
        }

        .g6-edge-hover {
            stroke: #ff6b35 !important;
            stroke-width: 3 !important;
            filter: drop-shadow(0 0 6px rgba(255,107,53,0.6));
        }

        .g6-edge-selected {
            stroke: #dc3545 !important;
            stroke-width: 3 !important;
            stroke-dasharray: 8,4 !important;
            animation: pulse-edge 2s infinite;
            filter: drop-shadow(0 0 8px rgba(220,53,69,0.6));
        }

        @keyframes pulse-edge {
            0% {
                opacity: 0.8;
                stroke-dasharray: 8,4;
            }
            50% {
                opacity: 1;
                stroke-dasharray: 4,2;
            }
            100% {
                opacity: 0.8;
                stroke-dasharray: 8,4;
            }
        }

        /* 统一容器的图表标题样式 */
        .chart-header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 10px 15px;
            border-radius: 8px 8px 0 0;
            font-size: 14px;
            font-weight: 600;
            display: flex;
            align-items: center;
            gap: 8px;
        }

        /* 图表说明文字 */
        .chart-instructions {
            background: #f8f9fa;
            border-left: 4px solid #007bff;
            padding: 8px 12px;
            margin: 10px 0;
            font-size: 12px;
            color: #6c757d;
            border-radius: 0 4px 4px 0;
        }
    </style>
</head>
<body>
    <div class="container-fluid p-1">
        <!-- 导航标签 -->
        <ul class="nav nav-tabs mb-2" id="mainTabs" role="tablist">
            <li class="nav-item" role="presentation">
                <button class="nav-link active px-3 py-1" id="import-tab" data-bs-toggle="tab" data-bs-target="#import-panel" type="button" role="tab">
                    <i class="bi bi-upload"></i> 表结构导入
                </button>
            </li>
            <li class="nav-item" role="presentation">
                <button class="nav-link px-3 py-1" id="mapping-tab" data-bs-toggle="tab" data-bs-target="#mapping-panel" type="button" role="tab">
                    <i class="bi bi-diagram-2"></i> 字段映射
                </button>
            </li>
            <li class="nav-item" role="presentation">
                <button class="nav-link px-3 py-1" id="sql-tab" data-bs-toggle="tab" data-bs-target="#sql-panel" type="button" role="tab">
                    <i class="bi bi-code-slash"></i> SQL生成
                </button>
            </li>
            <li class="nav-item" role="presentation">
                <button class="nav-link px-3 py-1" id="history-tab" data-bs-toggle="tab" data-bs-target="#history-panel" type="button" role="tab">
                    <i class="bi bi-clock-history"></i> 历史记录
                </button>
            </li>
        </ul>

        <div class="tab-content" id="mainTabContent">
            <!-- 导入面板 -->
            <div class="tab-pane fade show active" id="import-panel" role="tabpanel">
                <div class="row">
                    <div class="col-md-6">
                        <div class="card">
                            <div class="card-header bg-primary text-white">
                                <h5><i class="bi bi-table"></i> 源表结构导入</h5>
                            </div>
                            <div class="card-body">
                                <div class="excel-import-area mb-3" id="aTableDropZone">
                                    <i class="bi bi-file-earmark-excel" style="font-size: 3rem; color: #007bff;"></i>
                                    <p class="mt-3">拖拽Excel文件到此处或点击选择文件</p>
                                    <input type="file" id="aTableFile" accept=".xlsx,.xls" class="d-none">
                                    <button class="btn btn-primary" onclick="document.getElementById('aTableFile').click()">
                                        选择文件
                                    </button>
                                </div>
                                <div id="aTableInfo" class="alert alert-info d-none">
                                    <strong>表名：</strong><span id="aTableName"></span><br>
                                    <strong>字段数量：</strong><span id="aFieldCount"></span>
                                </div>
                                <div class="mt-3">
                                    <h6>批量添加字段说明：</h6>
                                    <textarea id="aTableComments" class="form-control batch-comment-input"
                                              placeholder="格式：字段名1：备注说明1&#10;字段名2：备注说明2"></textarea>
                                    <button class="btn btn-sm btn-outline-primary mt-2" onclick="applyComments('A')">
                                        应用说明
                                    </button>
                                </div>
                            </div>
                        </div>
                    </div>

                    <div class="col-md-6">
                        <div class="card">
                            <div class="card-header bg-success text-white">
                                <h5><i class="bi bi-table"></i> 目标表结构导入</h5>
                            </div>
                            <div class="card-body">
                                <div class="excel-import-area mb-3" id="bTableDropZone">
                                    <i class="bi bi-file-earmark-excel" style="font-size: 3rem; color: #28a745;"></i>
                                    <p class="mt-3">拖拽Excel文件到此处或点击选择文件</p>
                                    <input type="file" id="bTableFile" accept=".xlsx,.xls" class="d-none">
                                    <button class="btn btn-success" onclick="document.getElementById('bTableFile').click()">
                                        选择文件
                                    </button>
                                </div>
                                <div id="bTableInfo" class="alert alert-success d-none">
                                    <strong>表名：</strong><span id="bTableName"></span><br>
                                    <strong>字段数量：</strong><span id="bFieldCount"></span>
                                </div>
                                <div class="mt-3">
                                    <h6>批量添加字段说明：</h6>
                                    <textarea id="bTableComments" class="form-control batch-comment-input"
                                              placeholder="格式：字段名1：备注说明1&#10;字段名2：备注说明2"></textarea>
                                    <button class="btn btn-sm btn-outline-success mt-2" onclick="applyComments('B')">
                                        应用说明
                                    </button>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="text-center mt-4">
                    <button class="btn btn-lg btn-primary" onclick="switchToMapping()" id="startMappingBtn" disabled>
                        <i class="bi bi-arrow-right-circle"></i> 开始字段映射
                    </button>
                </div>
            </div>

            <!-- 映射面板 -->
            <div class="tab-pane fade" id="mapping-panel" role="tabpanel">
                <div class="row mb-2">
                    <div class="col-12">
                        <div class="mapping-controls">
                            <div class="row align-items-center">
                                <div class="col-md-4">
                                    <div class="btn-group" role="group">
                                        <button class="btn btn-primary btn-sm" onclick="autoMatchFields()">
                                            <i class="bi bi-magic"></i> 自动匹配
                                        </button>
                                        <button class="btn btn-warning btn-sm" onclick="clearAllMappings()">
                                            <i class="bi bi-arrow-clockwise"></i> 清除映射
                                        </button>
                                        <button class="btn btn-info btn-sm" onclick="saveMappingFromInterface()">
                                            <i class="bi bi-save"></i> 保存映射
                                        </button>
                                        <button class="btn btn-success btn-sm" onclick="generateSQL()">
                                            <i class="bi bi-code-slash"></i> 生成SQL
                                        </button>
                                    </div>
                                </div>
                                <div class="col-md-8 text-end">
                                    <small class="text-muted">
                                        <i class="bi bi-info-circle"></i>
                                        拖拽右侧源表字段到左侧目标表字段建立映射 | 同侧拖拽调整字段顺序
                                    </small>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="unified-mapping-container">
                    <!-- 表格映射容器 -->
                    <div class="table-mapping-container">
  
                        <div class="mapping-content">
                            <!-- 左侧目标表区域 (70%) -->
                            <div class="target-fields-area">
                                <div class="area-header">
                                    <h6><i class="bi bi-table"></i> 目标表字段</h6>
                                    <div class="filter-controls">
                                        <input type="text" class="form-control form-control-sm search-input"
                                               id="targetSearchInput" placeholder="搜索字段名或注释..."
                                               oninput="filterTargetFields(this.value)">
                                        <div class="btn-group btn-group-sm" role="group">
                                            <input type="radio" class="btn-check" name="targetFilter" id="targetAll" value="all" checked onchange="filterTargetFieldsByType('all')">
                                            <label class="btn btn-outline-secondary" for="targetAll">全部</label>
                                            <input type="radio" class="btn-check" name="targetFilter" id="targetMatched" value="matched" onchange="filterTargetFieldsByType('matched')">
                                            <label class="btn btn-outline-success" for="targetMatched">已匹配</label>
                                            <input type="radio" class="btn-check" name="targetFilter" id="targetUnmatched" value="unmatched" onchange="filterTargetFieldsByType('unmatched')">
                                            <label class="btn btn-outline-warning" for="targetUnmatched">未匹配</label>
                                        </div>
                                        <button class="btn btn-sm btn-success" onclick="openBatchCommentModal('B')" title="批量设置注释">
                                            <i class="bi bi-pencil-square"></i> 批量注释
                                        </button>
                                    </div>
                                </div>
                                <div class="fields-table-container">
                                    <table class="table table-hover fields-table" id="targetFieldsTable">
                                        <thead class="table-light">
                                            <tr>
                                                <th width="60px">操作</th>
                                                <th width="150px">字段名</th>
                                                <th width="120px">类型</th>
                                                <th>注释</th>
                                                <th width="200px">匹配的源字段</th>
                                            </tr>
                                        </thead>
                                        <tbody id="targetFieldsTableBody">
                                            <!-- 目标表字段将在这里动态生成 -->
                                        </tbody>
                                    </table>
                                </div>
                            </div>

                            <!-- 右侧源表区域 (30%) -->
                            <div class="source-fields-area">
                                <div class="area-header">
                                    <h6><i class="bi bi-table"></i> 源表字段</h6>
                                    <div class="filter-controls">
                                        <input type="text" class="form-control form-control-sm search-input"
                                               id="sourceSearchInput" placeholder="搜索字段名或注释..."
                                               oninput="filterSourceFields(this.value)">
                                        <button class="btn btn-sm btn-success" onclick="openBatchCommentModal('A')" title="批量设置注释">
                                            <i class="bi bi-pencil-square"></i> 批量注释
                                        </button>
                                    </div>
                                </div>
                                <div class="source-fields-list" id="sourceFieldsList">
                                    <!-- 源表字段将在这里动态生成 -->
                                </div>
                            </div>
                        </div>
                    </div>

                  </div>
            </div>

            <!-- SQL生成面板 -->
            <div class="tab-pane fade" id="sql-panel" role="tabpanel">
                <div class="row">
                    <div class="col-12">
                        <div class="card">
                            <div class="card-header">
                                <h5><i class="bi bi-code-slash"></i> 生成的SQL模板</h5>
                            </div>
                            <div class="card-body">
                                <!-- SQL模板配置区域 -->
                                <div class="row mb-4">
                                    <div class="col-12">
                                        <div class="card bg-light">
                                            <div class="card-header py-2">
                                                <h6 class="mb-0">
                                                    <i class="bi bi-gear"></i> SQL模板配置
                                                    <button class="btn btn-sm btn-outline-secondary float-end" onclick="toggleTemplateConfig()">
                                                        <i class="bi bi-chevron-down" id="templateConfigIcon"></i> 展开/收起
                                                    </button>
                                                </h6>
                                            </div>
                                            <div class="card-body" id="templateConfigPanel" style="display: none;">
                                                <div class="row">
                                                    <div class="col-md-6">
                                                        <label class="form-label">字符串类型（非空）</label>
                                                        <textarea class="form-control form-control-sm" id="stringNotNullTemplate" rows="2"
                                                                  placeholder="'${json.field_name}'"
                                                                  onchange="updateTemplateAndRegenerate('string_notnull', this.value)"></textarea>
                                                        <small class="form-text text-muted">非空字符串字段模板</small>
                                                    </div>
                                                    <div class="col-md-6">
                                                        <label class="form-label">字符串类型（允许为空）</label>
                                                        <textarea class="form-control form-control-sm" id="stringNullableTemplate" rows="2"
                                                                  placeholder="'${json.field_name!}'"
                                                                  onchange="updateTemplateAndRegenerate('string_nullable', this.value)"></textarea>
                                                        <small class="form-text text-muted">允许为空的字符串字段模板</small>
                                                    </div>
                                                </div>
                                                <div class="row mt-3">
                                                    <div class="col-md-6">
                                                        <label class="form-label">数值类型（非空）</label>
                                                        <textarea class="form-control form-control-sm" id="numericNotNullTemplate" rows="2"
                                                                  placeholder="${json.field_name}"
                                                                  onchange="updateTemplateAndRegenerate('numeric_notnull', this.value)"></textarea>
                                                        <small class="form-text text-muted">非空数值字段模板</small>
                                                    </div>
                                                    <div class="col-md-6">
                                                        <label class="form-label">数值类型（允许为空）</label>
                                                        <textarea class="form-control form-control-sm" id="numericNullableTemplate" rows="2"
                                                                  placeholder="<#if json.field_name??>${json.field_name}<#else>NULL</#if>"
                                                                  onchange="updateTemplateAndRegenerate('numeric_nullable', this.value)"></textarea>
                                                        <small class="form-text text-muted">允许为空的数值字段模板</small>
                                                    </div>
                                                </div>
                                                <div class="row mt-3">
                                                    <div class="col-md-6">
                                                        <label class="form-label">时间类型（非空）</label>
                                                        <textarea class="form-control form-control-sm" id="dateNotNullTemplate" rows="2"
                                                                  placeholder="cast('${json.field_name}' as datetime)"
                                                                  onchange="updateTemplateAndRegenerate('date_notnull', this.value)"></textarea>
                                                        <small class="form-text text-muted">非空时间字段模板</small>
                                                    </div>
                                                    <div class="col-md-6">
                                                        <label class="form-label">时间类型（允许为空）</label>
                                                        <textarea class="form-control form-control-sm" id="dateNullableTemplate" rows="2"
                                                                  placeholder="<#if json.field_name??>cast('${json.field_name}' as datetime)<#else>NULL</#if>"
                                                                  onchange="updateTemplateAndRegenerate('date_nullable', this.value)"></textarea>
                                                        <small class="form-text text-muted">允许为空的时间字段模板</small>
                                                    </div>
                                                </div>
                                                <div class="row mt-3">
                                                    <div class="col-12">
                                                        <label class="form-label">整体拼接模板</label>
                                                        <textarea class="form-control form-control-sm" id="mergeTemplate" rows="3"
                                                                  placeholder="insert into 【TABLE_NAME】 [INSERT_VALUES_FORMAT] ON CONFLICT (yljgdm, jclsh, xgbz) DO UPDATE SET [MERGE_SET_FORMAT]"
                                                                  onchange="updateTemplateAndRegenerate('merge_template', this.value)"></textarea>
                                                        <small class="form-text text-muted">【TABLE_NAME】会被替换为实际表名，[INSERT_VALUES_FORMAT]和[MERGE_SET_FORMAT]会被替换为对应格式</small>
                                                    </div>
                                                </div>
                                                <div class="mt-3">
                                                    <button class="btn btn-sm btn-outline-primary" onclick="resetTemplates()">
                                                        <i class="bi bi-arrow-clockwise"></i> 重置为默认模板
                                                    </button>
                                                    <button class="btn btn-sm btn-outline-success" onclick="saveTemplates()">
                                                        <i class="bi bi-save"></i> 保存模板配置
                                                    </button>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                </div>

                                <div class="mb-3">
                                    <button class="btn btn-primary btn-sm" onclick="copyAllSQL()">
                                        <i class="bi bi-clipboard"></i> 复制全部SQL
                                    </button>
                                    <button class="btn btn-success btn-sm ms-2" onclick="saveMapping()">
                                        <i class="bi bi-save"></i> 保存映射关系
                                    </button>
                                </div>

                                <h6 class="mt-4">1. INSERT VALUES格式：</h6>
                                <div id="sqlValues" class="sql-output"></div>

                                <h6 class="mt-4">2. UPDATE SET格式：</h6>
                                <div id="sqlUpdate" class="sql-output"></div>

                                <h6 class="mt-4">3. MERGE SET格式（带标记）：</h6>
                                <div id="sqlMerge" class="sql-output"></div>

                                <h6 class="mt-4">4. 完整SQL拼接格式：</h6>
                                <div id="sqlFull" class="sql-output"></div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>

            <!-- 历史记录面板 -->
            <div class="tab-pane fade" id="history-panel" role="tabpanel">
                <div class="row mb-3">
                    <div class="col-12">
                        <div class="card bg-light">
                            <div class="card-body">
                                <div class="row align-items-center">
                                    <div class="col-md-7">
                                        <h6 class="mb-2"><i class="bi bi-bar-chart"></i> 数据统计</h6>
                                        <p class="text-muted mb-0" id="dataStats">正在加载数据统计...</p>
                                    </div>
                                    <div class="col-md-5 text-end">
                                        <input type="file" id="importDbFile" accept=".json" class="d-none">
                                        <button class="btn btn-outline-primary btn-sm me-2" onclick="document.getElementById('importDbFile').click()">
                                            <i class="bi bi-upload"></i> 导入数据
                                        </button>
                                        <button class="btn btn-primary btn-sm me-2" onclick="exportDatabase()">
                                            <i class="bi bi-download"></i> 导出数据
                                        </button>
                                        <button class="btn btn-success btn-sm" onclick="createBackup()">
                                            <i class="bi bi-shield-check"></i> 创建备份
                                        </button>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="row mb-3">
                    <div class="col-md-3">
                        <div class="card text-center border-primary">
                            <div class="card-body">
                                <h5 class="card-title text-primary" id="totalMappings">0</h5>
                                <p class="card-text">映射记录</p>
                            </div>
                        </div>
                    </div>
                    <div class="col-md-3">
                        <div class="card text-center border-success">
                            <div class="card-body">
                                <h5 class="card-title text-success" id="totalTables">0</h5>
                                <p class="card-text">表数量</p>
                            </div>
                        </div>
                    </div>
                    <div class="col-md-3">
                        <div class="card text-center border-info">
                            <div class="card-body">
                                <h5 class="card-title text-info" id="storageSize">0 KB</h5>
                                <p class="card-text">存储大小</p>
                            </div>
                        </div>
                    </div>
                    <div class="col-md-3">
                        <div class="card text-center border-warning">
                            <div class="card-body">
                                <h5 class="card-title text-warning" id="lastBackupTime">从未</h5>
                                <p class="card-text">最后备份</p>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="row">
                    <div class="col-12">
                        <div class="card">
                            <div class="card-header d-flex justify-content-between align-items-center">
                                <h5 class="mb-0"><i class="bi bi-clock-history"></i> 历史映射记录</h5>
                                <div>
                                    <button class="btn btn-sm btn-outline-warning me-2" onclick="debugLocalStorage()">
                                        <i class="bi bi-bug"></i> 调试存储
                                    </button>
                                    <button class="btn btn-sm btn-outline-danger" onclick="clearAllHistory()">
                                        <i class="bi bi-trash"></i> 清空历史
                                    </button>
                                </div>
                            </div>
                            <div class="card-body">
                                <div class="table-responsive">
                                    <table class="table table-striped" id="historyTable">
                                        <thead>
                                            <tr>
                                                <th width="20%">创建时间</th>
                                                <th width="25%">源表名</th>
                                                <th width="25%">目标表名</th>
                                                <th width="10%">映射数量</th>
                                                <th width="20%">操作</th>
                                            </tr>
                                        </thead>
                                        <tbody id="historyTableBody">
                                            <!-- 历史记录将在这里显示 -->
                                        </tbody>
                                    </table>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <!-- 数据库路径显示 -->
    <div id="dbInfo" class="db-info">
        <i class="bi bi-database"></i> 数据库: <span id="dbPath">浏览器本地存储</span>
    </div>

    <!-- 模态框：编辑字段 -->
    <div class="modal fade" id="editFieldModal" tabindex="-1">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title">编辑字段信息</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
                </div>
                <div class="modal-body">
                    <form id="editFieldForm">
                        <div class="mb-3">
                            <label class="form-label">字段名</label>
                            <input type="text" class="form-control" id="editFieldName" readonly>
                        </div>
                        <div class="mb-3">
                            <label class="form-label">注释说明</label>
                            <textarea class="form-control" id="editFieldComment" rows="3"></textarea>
                        </div>
                        <div class="mb-3">
                            <label class="form-label">数据类型</label>
                            <input type="text" class="form-control" id="editFieldType">
                        </div>
                        <div class="form-check">
                            <input class="form-check-input" type="checkbox" id="editFieldRequired">
                            <label class="form-check-label" for="editFieldRequired">
                                非空
                            </label>
                        </div>
                    </form>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
                    <button type="button" class="btn btn-primary" onclick="saveFieldEdit()">保存</button>
                </div>
            </div>
        </div>
    </div>

  
    <!-- 右键菜单：删除连线 -->
    <div id="connectionContextMenu" class="dropdown-menu" style="position: absolute; display: none; z-index: 1000;">
        <button class="dropdown-item" onclick="deleteMappingByConnection()">
            <i class="bi bi-trash text-danger"></i> 删除映射
        </button>
    </div>

    
    <!-- Scripts -->
    <!-- 编辑注释模态框 -->
    <div class="modal fade" id="editCommentModal" tabindex="-1" aria-labelledby="editCommentModalLabel" aria-hidden="true">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title" id="editCommentModalLabel">编辑字段注释</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                </div>
                <div class="modal-body">
                    <div class="mb-3">
                        <label for="editCommentFieldName" class="form-label">字段名称</label>
                        <input type="text" class="form-control" id="editCommentFieldName" readonly>
                    </div>
                    <div class="mb-3">
                        <label for="editComment" class="form-label">字段注释</label>
                        <textarea class="form-control" id="editComment" rows="3" placeholder="请输入字段注释..."></textarea>
                    </div>
                    <input type="hidden" id="editTableType">
                    <input type="hidden" id="editOriginalFieldName">
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
                    <button type="button" class="btn btn-primary" onclick="saveComment()">保存</button>
                </div>
            </div>
        </div>
    </div>

    <!-- 批量设置注释模态框 -->
    <div class="modal fade" id="batchCommentModal" tabindex="-1" aria-labelledby="batchCommentModalLabel" aria-hidden="true">
        <div class="modal-dialog modal-lg">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title" id="batchCommentModalLabel">批量设置注释</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                </div>
                <div class="modal-body">
                    <div class="mb-3">
                        <label for="batchCommentText" class="form-label">字段注释（格式：字段名:注释说明）</label>
                        <textarea class="form-control" id="batchCommentText" rows="12" placeholder="请按以下格式输入字段注释：&#10;字段名1：备注说明1&#10;字段名2：备注说明2&#10;字段名3：备注说明3"></textarea>
                        <div class="form-text">
                            <small class="text-muted">注释为空的字段会显示在前面，已有注释的字段显示在后面。注释为[NULL]表示空值，直接显示字段名。</small>
                        </div>
                    </div>
                    <div class="mb-3">
                        <label for="batchTableType" class="form-label">应用范围</label>
                        <select class="form-select" id="batchTableType">
                            <option value="B">目标表字段（左侧）</option>
                            <option value="A">源表字段（右侧）</option>
                        </select>
                    </div>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
                    <button type="button" class="btn btn-primary" onclick="saveBatchComment()">应用注释</button>
                </div>
            </div>
        </div>
    </div>

    <script src="./bootstrap.bundle.min.js"></script>
    <script src="./xlsx.full.min.js"></script>
    <script src="./sql-wasm.js"></script>
    <script src="./d3.v7.min.js"></script>
    <script src="app.js"></script>
</body>
</html>